/**
 * 
 */
package neptune.manager.allocator;

import java.io.File;
import java.util.Map;
import neptune.manager.NeptuneManager;
import neptune.manager.connector.Connector;
import neptune.manager.connector.ConnectorException;
import neptune.manager.model.cluster.Cluster;
import neptune.manager.model.cluster.PhysicalNode;
import neptune.manager.model.topology.Node;
import neptune.manager.model.topology.Topology;
import neptune.manager.storage.StorageReadException;
import neptune.manager.utils.DebugLog;

/**
 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
 * 
 */
public class MainAllocator implements Allocator, Runnable {

	private String vmTemplateSourcePath;
	private String experimentsVMsPath;
	
	private int operation;
	private final static int OPERATION_NULL=0;
	private final static int OPERATION_ALLOCATE=1;
	private final static int OPERATION_ALLOCATE_WITH_MAP=2;
	

	private String emuPluginRootPath;
	
	public MainAllocator(String emuPluginRootPath, String vmTemplatesPath,
			String experimentsVmsPath) {
		super();
		this.emuPluginRootPath=emuPluginRootPath;
		this.operation=OPERATION_NULL;
		if (!vmTemplatesPath.endsWith(File.separator))
			vmTemplatesPath += File.separator;
		if (!emuPluginRootPath.endsWith(File.separator))
			emuPluginRootPath += File.separator;
		if (!experimentsVmsPath.endsWith(File.separator))
			experimentsVmsPath += File.separator;

		vmTemplateSourcePath = vmTemplatesPath;
		this.experimentsVMsPath = experimentsVmsPath;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * neptune.manager.allocator.Allocator#allocate(neptune.manager.model.topology
	 * .Topology)
	 */
	@Override
	public void allocate(String expId, Topology topology, Cluster cluster)
			throws AllocationException 
	{
		AsyncExperimentAllocator allocator;
		try 
		{
			allocator = new AsyncExperimentAllocator(this.emuPluginRootPath, this.vmTemplateSourcePath,
					this.experimentsVMsPath, expId, topology, cluster);
		}
		catch (StorageReadException e) 
		{
			throw new AllocationException(e);
		}
		Thread t=new Thread(allocator);
		t.start();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * neptune.manager.allocator.Allocator#deallocate(neptune.manager.model.
	 * topology.Topology)
	 */
	@Override
	public void deallocate(String expId, Topology topology, Cluster cluster)
			throws DeallocationException {
		for (Node node : topology.getNodes()) {

			String nodeFullId = node.getId()
					+ NeptuneManager.EXPID_NODEID_SEPARATOR + expId;

			for (PhysicalNode phyNode : cluster.getNodes()) {
				if (phyNode.isChildren(nodeFullId)) {
					Connector connector = null;
					try {
						connector = NeptuneManager.instance().getConnector(
								phyNode.getId());
						connector.swapOutVM(nodeFullId);
						phyNode.removeHostedNode(node, topology.getName());

					} catch (ConnectorException e) {
						DebugLog.printDetailedLog(e.getMessage(),
								MainAllocator.class.getSimpleName());
						throw new DeallocationException(e);
					} finally {
						if (connector != null)
							try {
								connector.closeConnection();
							} catch (ConnectorException e) {
								DebugLog.printDetailedLog(e.getMessage(),
										MainAllocator.class.getSimpleName());
								throw new RuntimeException(e);
							}
					}
				}
			}
		}
	}

	@Override
	public void isTopologyAllocable(Topology topology, Cluster cluster)
			throws AllocabilityException 
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void allocateWithAllocationMap(Map<String, String[]> allocationMap,
			Topology topology, Cluster cluster) throws AllocationException 	
	{
		AsyncExperimentAllocatorWithMap allocator;
		try
		{
			allocator = new	AsyncExperimentAllocatorWithMap (
					this.emuPluginRootPath, this.vmTemplateSourcePath,
					this.experimentsVMsPath, allocationMap, topology, cluster );
		}
		catch (StorageReadException e) 
		{
			throw new AllocationException(e);
		}
		Thread t=new Thread(allocator);
		t.start();
	}

	@Override
	public void run() 
	{
		switch (this.operation)
		{
			case OPERATION_ALLOCATE:
				break;
			case OPERATION_ALLOCATE_WITH_MAP:
				break;
			default:
				break;
		}
	}

	@Override
	public void terminate(String expId, Topology topology, Cluster cluster)
			throws TerminationException 
	{
		for (Node node : topology.getNodes()) 
		{

			String nodeFullId = node.getId()
					+ NeptuneManager.EXPID_NODEID_SEPARATOR + expId;

			for (PhysicalNode phyNode : cluster.getNodes()) 
			{
				if (phyNode.isChildren(nodeFullId)) 
				{
					Connector connector = null;
					try
					{
						connector = NeptuneManager.instance().getConnector(
								phyNode.getId());
						connector.TerminateVM(nodeFullId);
						phyNode.removeHostedNode(node, topology.getName());

					} 
					catch (ConnectorException e) 
					{
						DebugLog.printDetailedLog(e.getMessage(),
								MainAllocator.class.getSimpleName());
						throw new TerminationException(e);
					}
					finally
					{
						if (connector != null)
						try 
						{
							connector.closeConnection();
						}
						catch (ConnectorException e) 
						{
							DebugLog.printDetailedLog(e.getMessage(),
									MainAllocator.class.getSimpleName());
							throw new RuntimeException(e);
						}
					}
				}
			}
		}
	}
}
